#include <stdint.h>
#include "stm32f4xx_hal.h"
#include "systick.h"

typedef struct
{
	GPIO_TypeDef *gpio;
	uint16_t pin;
	GPIO_PinState invalid_level;
} Key_GPIO_t;

static Key_GPIO_t g_gpioList[] =
{
	{GPIOC, GPIO_PIN_0, GPIO_PIN_SET},
	{GPIOC, GPIO_PIN_1, GPIO_PIN_SET},
};

#define KEY_NUM_MAX (sizeof(g_gpioList) / sizeof(g_gpioList[0]))

// 按键状态机事件
typedef enum
{
	eNo_KeyPress_t = 0, // 无按键
	eKeyShortPress_t, // 短按
	eKeyDownShake_t, // 按下抖动
	eKeyUpShake_t, // 弹起抖动
	eKeyUp_t, // 按键释放
	eKeyLongPress_t, // 长按
} KeyScanStatus;

// 按键状态
typedef struct
{
	KeyScanStatus status;
	uint64_t timeout;
} KeyState_t;
KeyState_t g_KeyState[KEY_NUM_MAX];

// 消抖时间
#define KEY_SHAKE_TIME		(20)	// 20ms
// 长按时间
#define KEY_LONG_PESS_TIME	(2000)  // 2s

/**
***********************************************************
* @brief 按键扫描
* @param index:按键编号
* @return 返回按键键值
***********************************************************
*/
static uint8_t Key_Scan(uint8_t index)
{
	uint8_t key_code  = 0x00;
	uint64_t currentTime = 0;
	uint8_t key_status;
	
	key_status = HAL_GPIO_ReadPin(g_gpioList[index].gpio, g_gpioList[index].pin);
	switch(g_KeyState[index].status)
	{
		case eNo_KeyPress_t: // 无按键
		{
			if(key_status)
			{
				g_KeyState[index].status = eKeyDownShake_t;
				g_KeyState[index].timeout = GetSysTime();
			}
		}
		break;
		
		case eKeyDownShake_t: // 按下抖动
		{
			currentTime = GetSysTime();
			// 如果时间超过抖动时间且按键电平为有效电平==>切换状态为短按
			if(key_status && (currentTime - g_KeyState[index].timeout > KEY_SHAKE_TIME))
			{
				g_KeyState[index].status = eKeyShortPress_t;
			}
		}
		break;
		
		case eKeyShortPress_t: // 短按
		{
			currentTime = GetSysTime();
			// 如果时间超过抖动时间且按键电平为有效电平==>切换状态为短按
			if(key_status && (currentTime - g_KeyState[index].timeout > KEY_LONG_PESS_TIME))
			{
				g_KeyState[index].status = eKeyLongPress_t;
			}
			else if(!key_status)
			{
				g_KeyState[index].timeout = GetSysTime();
				g_KeyState[index].status = eKeyUpShake_t;
			}
			key_code = 0x01 + index;
		}
		break;
		
		case eKeyUpShake_t: // 弹起抖动
		{			
			currentTime = GetSysTime();
			// 如果时间超过抖动时间且按键电平为有效电平==>切换状态为短按
			if(!key_status && (currentTime - g_KeyState[index].timeout > KEY_SHAKE_TIME))
			{
				g_KeyState[index].status = eKeyUp_t;
			}
		}
		break;
		
		case eKeyUp_t: // 按键释放
		{
			key_code = 0x00;
			g_KeyState[index].status = eNo_KeyPress_t;
		}
		break;
		
		case eKeyLongPress_t: // 长按
		{
			key_code = 0x81 + index;
			if(!key_status)
			{
				g_KeyState[index].timeout = GetSysTime();
				g_KeyState[index].status = eKeyUpShake_t;
			}
		}
		break;
	}
	
	return key_code;
}

/**
***********************************************************
* @brief 按键硬件初始化
* @param
* @return 
***********************************************************
*/
void KeyDrvInit(void)
{
	GPIO_InitTypeDef GPIO_Init = { 0 };
	
	// 时钟初始化
	__HAL_RCC_GPIOC_CLK_ENABLE();
	
	// 默认配置
	GPIO_Init.Mode = GPIO_MODE_INPUT;
	GPIO_Init.Pull = GPIO_NOPULL;
	GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;
	
	for (uint8_t i = 0; i < KEY_NUM_MAX; i++)
	{
		GPIO_Init.Pin = g_gpioList[i].pin; // 引脚配置	
		HAL_GPIO_Init(g_gpioList[i].gpio, &GPIO_Init); // 初始化
	}
}

/**
***********************************************************
* @brief 获取按键码值
* @param
* @return 两个按键码值，短按0x01 0x02，
			长按0x81 0x82，没有按下为0
***********************************************************
*/
uint8_t GetKeyVal(void)
{
	uint8_t val = 0;
	
	for(int i = 0; i < KEY_NUM_MAX; i++)
	{
		val = Key_Scan(i);
		//p_dbg("key value:%d", val);
		if(val) // 检测到按键退出扫描
		{
			break;
		}
	}
	return val;
}
